// Copyright (C) 2005 Taylor L. Riché <riche@cs.utexas.edu>
//  
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software 
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
//
// 	$Id: ApplicationGraph.cc 252 2005-10-03 17:03:50Z richetl $	
//

#include "ApplicationGraph.hh"

using namespace lee;

void
ApplicationGraph::registerFunctionSubs(Function * function)
{
  Subscription * sub = NULL;
  if((sub = dynamic_cast<Subscription *>(function)) != NULL) {
    assert(sub != NULL);
    sub->registerSubs(dynamic_cast<Subscription *>(this));
  }
  return;
}

void
ApplicationGraph::setCMap(std::map<pthread_t, CourierUnit*> const & cmap)
{
  for(std::map<std::string, Function*>::iterator i = functionMap.begin();
      i != functionMap.end(); i++) {
    Subscription * sub;
    if((sub = dynamic_cast<Subscription *>(i->second)) != NULL) {
      sub->setMap(&cmap);
    }
  }
  return;
}

Function * 
ApplicationGraph::getNextFunction(std::string functionName, int exitPoint) 
{
  Synchronized::Scope mutex(this);
  std::map<int, Function *> * target;
  while(appGraph.count(functionName) == 0) {
    wait();
  }
  while(appGraph[functionName] == NULL) {
    wait();
  }
  while(appGraph[functionName]->count(exitPoint) == 0) {
    wait();
  }
  target = appGraph[functionName];
  return (*target)[exitPoint];
}

void
ApplicationGraph::makeConnection(std::string leftFunction, int leftPort, std::string rightFunction)
{
  Synchronized::Scope mutex(this);
  if(appGraph[rightFunction] == NULL) {
    appGraph[rightFunction] = new std::map<int, Function*>;
  }
  while(functionMap[rightFunction] == NULL) {
    wait();
  }
  assert(appGraph[leftFunction] != NULL);
  (*appGraph[leftFunction])[leftPort] = functionMap[rightFunction];
  notify();
  return;
}

void 
ApplicationGraph::addFunctionAfter(std::string origFunction, int origPort, 
			      std::string newFunction, int newPort)
{
  Synchronized::Scope mutex(this);

  while(functionMap[newFunction] == NULL) {
    wait();
  }
  if(appGraph[newFunction] == NULL) {
    appGraph[newFunction] = new std::map<int, Function*>;
  }
  Function * oldFunction = (*appGraph[origFunction])[origPort];
  (*appGraph[newFunction])[newPort] = oldFunction;
  (*appGraph[origFunction])[origPort] = functionMap[newFunction];
  sched->addFunc(functionMap[newFunction]);
  return;
}

void 
ApplicationGraph::addFunctionBefore(std::string newFunction, int newPort, std::string origFunction)
{
  Synchronized::Scope mutex(this);
  Function * newSink;
  while((newSink = functionMap[newFunction]) == NULL) {
    wait();
  }
  if(appGraph[newFunction] == NULL) {
    appGraph[newFunction] = new std::map<int, Function*>;
  }
  std::list<std::pair<std::string, int> > * srcList = 
    getSources(origFunction);
  assert(!srcList->empty()); // FIXME this needs to be handled better
  for(std::list<std::pair<std::string, int> >::iterator i = srcList->begin();
      i != srcList->end(); i++) {
    (*appGraph[i->first])[i->second] = newSink;
  }  
  Function * origSink = functionMap[origFunction];
  assert(origSink != NULL);
  assert(appGraph[newFunction] != NULL);
  (*appGraph[newFunction])[newPort] = origSink;
  sched->addFunc(newSink);
  return;
}

void
ApplicationGraph::deleteFunction(std::string functionName, int port)
{
  Synchronized::Scope mutex(this);
  std::list<std::pair<std::string, int> > * srcList = 
    getSources(functionName);
  Function * newSink = (*appGraph[functionName])[port];
  assert(newSink != NULL); //FIXME handle this
  for(std::list<std::pair<std::string, int> >::iterator i = srcList->begin();
      i != srcList->end(); i++) {
    (*appGraph[i->first])[i->second] = newSink;
  }
  functionMap.erase(functionName);
  assert(functionMap.count(functionName) == 0);
  return;
}

std::list<std::pair<std::string, int> > * 
ApplicationGraph::getSources(std::string const & sink)
{
  // lock already held, no need to grab new one
  std::list<std::pair<std::string, int> > * retList = 
    new std::list<std::pair<std::string, int> >;
  Function * sinkPtr = functionMap[sink];
  for(std::map<std::string, std::map<int, Function*> *>::iterator i = appGraph.begin();
      i != appGraph.end(); i++) {
    for(std::map<int, Function *>::iterator j = i->second->begin();
	j != i->second->end(); j++) {
      if(j->second == sinkPtr) {
	std::pair<std::string, int> newPair;
	newPair.first = i->first;
	newPair.second = j->first;
	retList->push_back(newPair);
      }
    }
  }
  return retList;
}
